package com.newegg.whois.controller;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.newegg.whois.model.DomainInfo;
import com.newegg.whois.search.DNSUtil;
import com.newegg.whois.search.WhoisUtil;
import com.newegg.whois.util.DomainUtil;
import com.newegg.whois.util.IPHelp;
import com.newegg.whois.util.RestTemplateUtil;

@RestController
@RequestMapping
public class SearchAPI {
	
	static final Logger logger = Logger.getLogger(SearchAPI.class);
	
	static long complete = 0;
	static long fail = 0;
	
	static Cache<String, Map<String, Object>> cache = CacheBuilder.newBuilder()
			.maximumSize(100000)
			.expireAfterWrite(30 * 24, TimeUnit.HOURS)
			.build();
	
	@Value("${search.location.url}")
	String searchLocationUrl;
	
	@RequestMapping(value="/query", method = RequestMethod.GET)
	@ResponseBody
	public Map<String, Object> query(@RequestParam String domain, @RequestParam(required=false, defaultValue="false") boolean force) throws Exception{
		DomainInfo info = DomainUtil.getDomainInfo(domain);
		if(info == null || info.getDomain() == null){
			Exception exception = new IllegalAccessException("domain [" + domain + "] is not matched");
			logger.error("query [" + domain + "]", exception);
			throw exception;
		}
		try {
			Map<String, Object> data = cache.getIfPresent(info.getDomainName());
			if(data == null || force){
				Map<String, String> lookup = DNSUtil.nslookup(info);
				data = WhoisUtil.query(info);
				data.putAll(lookup);
				String ip = lookup.get("Lookup.IP");
				if(ip != null && !"".equals(ip.trim())){
					Map<String, Object> location = getIpLocation(ip.trim(), false);
					data.putAll(location);
				}
				cache.put(info.getDomainName(), data);
				complete++;
			}
			return data;
		} catch (Exception e) {
			logger.error("query [" + domain + "]", e);
			fail++;
			throw e;
		}
	}
	
	@RequestMapping(value="/ipLocation", method = RequestMethod.GET)
	@ResponseBody
	public Map<String, Object> getIpLocation(@RequestParam String IP, @RequestParam(required=false, defaultValue="false") boolean force) throws Exception {
		String ipString = IP.toString().trim();
		Map<String, Object> data = null;
		if(!force){ data = cache.getIfPresent("IP:" + ipString); }
		if(data == null){ data = new HashMap<>(); }
		if(!IPHelp.isLocal(ipString) && !"".equals(ipString)){
			String location = RestTemplateUtil.get(searchLocationUrl.replace("{ip}", ipString));
			if(location != null){
				JSONObject json = JSON.parseObject(location).getJSONObject(ipString);
				data.put("location.region", json.getString("region"));
				data.put("location.cyc", json.getString("cyc"));
				data.put("location.city", json.getString("city"));
				data.put("location.pcode", json.getString("pcode"));
				data.put("location.lat", json.getString("lat"));
				data.put("location.lon", json.getString("lon"));
				data.put("location.cyn", json.getString("cyn"));
				data.put("location.org", json.getString("org"));
				cache.put("IP:" + ipString, data);
			}
		}
		return data;
	}
}
